// Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	goscanner "go/scanner"
	"go/token"
	"io"
	"path"
	"runtime"
	"strconv"
	"text/scanner"

	"code.google.com/p/go.exp/ebnf"
	"github.com/cznic/strutil"
)

func getopts(src []byte) (o *options, err error) {
	const (
		from = "/*{\n"
		to   = "\n}*/\n"
	)
	a := bytes.Index(src, []byte(from))
	if a < 0 || a > 0 && src[a-1] != '\n' {
		return
	}

	b := bytes.Index(src[a+len(from):], []byte(to))
	if b < 0 {
		return
	}

	b += a + len(from) + 2
	a += 2
	o = &options{}
	if err = json.Unmarshal(src[a:b], o); err != nil {
		o = nil
	}
	return
}

func do(start, fname string, src []byte, out io.Writer) (err error) {
	var errv goscanner.ErrorList
	buf := &bytes.Buffer{}
	job := &task{
		errf: func(pos scanner.Position, msg string) {
			errv.Add(token.Position(pos), msg)
		},
		f:     strutil.IndentFormatter(buf, "\t"),
		names: map[string]bool{},
		start: start,
		todos: *optTodos,
	}
	if job.grm, err = ebnf.Parse(fname, bytes.NewBuffer(src)); err != nil {
		return
	}

	if err = ebnf.Verify(job.grm, start); err != nil {
		return
	}

	if job.opts, err = getopts(src); err != nil {
		return
	}

	if job.opts == nil {
		return fmt.Errorf("Missing JSON options")
	}

	job.rewriteRepetitions()
	job.flatten()
	job.prolog()
	job.emit()
	if len(errv) != 0 {
		errv.Sort()
		return errv
	}

	_, err = out.Write(buf.Bytes())
	return
}

// me reports its calling site as a string.
func me() string {
	_, file, line, ok := runtime.Caller(1)
	if !ok {
		return "???"
	}

	return path.Base(file) + ":" + strconv.Itoa(line)
}
